<template>
	<view class='image-cropper' catchtouchmove='_preventTouchMove'>
		<view class='main' bindtouchend="_cutTouchEnd" bindtouchstart="_cutTouchStart" bindtouchmove="_cutTouchMove" bindtap="_click">
			<view class='content'>
				<!-- <view class='content_top bg_gray' :class="{bg_black:data._flag_bright}" :style="{height:data.cut_top + 'px';transitionProperty:data._cut_animation?'':'background'}"></view> -->
				<view class='content_middle' :style="{height:data.cut_top + 'px'}">
					<!-- <view class='content_middle_left bg_gray {{_flag_bright?"":"bg_black"}}' style="width:{{cut_left}}px;transition-property:{{_cut_animation?'':'background'}}"></view> -->
					<!-- <view class='content_middle_middle' style="width:{{width}}px;height:{{height}}px;transition-duration: .3s;transition-property:{{_cut_animation?'':'background'}};">
						<view class="border border-top-left"></view>
						<view class="border border-top-right"></view>
						<view class="border border-right-top"></view>
						<view class="border border-right-bottom"></view>
						<view class="border border-bottom-right"></view>
						<view class="border border-bottom-left"></view>
						<view class="border border-left-bottom"></view>
						<view class="border border-left-top"></view>
					</view> -->
					<!-- <view class='content_middle_right bg_gray {{_flag_bright?"":"bg_black"}}' style="transition-property:{{_cut_animation?'':'background'}}"></view> -->
				</view>
				<!-- <view class='content_bottom bg_gray {{_flag_bright?"":"bg_black"}}' style="transition-property:{{_cut_animation?'':'background'}}"></view> -->
			</view>
			<image bindload="imageLoad" bindtouchstart="_start" bindtouchmove="_move" bindtouchend="_end" :style="{width: data.img_width ? data.img_width + 'px' : 'auto' ,height: data.img_height ? data.img_height + 'px' : 'auto',transform:'translate3d(' + _img_left-img_width/2 + 'px,' + data._img_top-img_height/2 + ',0) scale(' + data.scale+ ') rotate(' +data.angle+ 'deg)',transitionDuration:_cut_animation?.4:0 + 's'}"
			 class='img' :src='data.imgSrc'></image>
		</view>
		<canvas canvas-id='image-cropper' disable-scroll="true" :style="{width:data._canvas_width * data.export_scale + 'px',height:data._canvas_height * data.export_scale + 'px',left:data.canvas_left + 'px',top:data.canvas_top + 'px'}"
		 class='image-cropper-canvas'></canvas>
	</view>
</template>

<script>
	export default {
		props: {
			/**
			 * 图片路径
			 */
			imgSrc: {
				type: String,
			},
			/**
			 * 裁剪框高度
			 */
			height: {
				type: Number,
				value: 200,
			},
			/**
			 * 裁剪框宽度
			 */
			width: {
				type: Number,
				value: 200,
			},
			/**
			 * 裁剪框最小尺寸
			 */
			min_width: {
				type: Number,
				value: 100,
			},
			min_height: {
				type: Number,
				value: 100,
			},
			/**
			 * 裁剪框最大尺寸
			 */
			max_width: {
				type: Number,
				value: 300,
			},
			max_height: {
				type: Number,
				value: 300,
			},
			/**
			 * 裁剪框禁止拖动
			 */
			disable_width: {
				type: Boolean,
				value: false,
			},
			disable_height: {
				type: Boolean,
				value: false,
			},
			/**
			 * 锁定裁剪框比例
			 */
			disable_ratio: {
				type: Boolean,
				value: false,
			},
			/**
			 * 生成的图片尺寸相对剪裁框的比例
			 */
			export_scale: {
				type: Number,
				value: 3,
			},
			/**
			 * 生成的图片质量0-1
			 */
			quality: {
				type: Number,
				value: 1,
			},
			cut_top: {
				type: Number,
				value: null,
			},
			cut_left: {
				type: Number,
				value: null,
			},
			/**
			 * canvas上边距（不设置默认不显示）
			 */
			canvas_top: {
				type: Number,
				value: null,
			},
			/**
			 * canvas左边距（不设置默认不显示）
			 */
			canvas_left: {
				type: Number,
				value: null,
			},
			/**
			 * 图片宽度
			 */
			img_width: {
				type: null,
				value: null,
			},
			/**
			 * 图片高度
			 */
			img_height: {
				type: null,
				value: null,
			},
			/**
			 * 图片缩放比
			 */
			scale: {
				type: Number,
				value: 1,
			},
			/**
			 * 图片旋转角度
			 */
			angle: {
				type: Number,
				value: 0,
			},
			/**
			 * 最小缩放比
			 */
			min_scale: {
				type: Number,
				value: 0.5,
			},
			/**
			 * 最大缩放比
			 */
			max_scale: {
				type: Number,
				value: 2,
			},
			/**
			 * 是否禁用旋转
			 */
			disable_rotate: {
				type: Boolean,
				value: false,
			},
			/**
			 * 是否限制移动范围(剪裁框只能在图片内)
			 */
			limit_move: {
				type: Boolean,
				value: false,
			},
		},
		data() {
			return {
				data:{
					el: 'image-cropper', //暂时无用
					info: wx.getSystemInfoSync(),
					MOVE_THROTTLE: null, //触摸移动节流settimeout
					MOVE_THROTTLE_FLAG: true, //节流标识
					INIT_IMGWIDTH: 0, //图片设置尺寸,此值不变（记录最初设定的尺寸）
					INIT_IMGHEIGHT: 0, //图片设置尺寸,此值不变（记录最初设定的尺寸）
					TIME_BG: null, //背景变暗延时函数
					TIME_CUT_CENTER: null,
					_touch_img_relative: [{
						x: 0,
						y: 0,
					}, ], //鼠标和图片中心的相对位置
					_flag_cut_touch: false, //是否是拖动裁剪框
					_hypotenuse_length: 0, //双指触摸时斜边长度
					_flag_img_endtouch: false, //是否结束触摸
					_flag_bright: true, //背景是否亮
					_canvas_overflow: true, //canvas缩略图是否在屏幕外面
					_canvas_width: 200,
					_canvas_height: 200,
					origin_x: 0.5, //图片旋转中心
					origin_y: 0.5, //图片旋转中心
					_cut_animation: false, //是否开启图片和裁剪框过渡
					_img_top: wx.getSystemInfoSync().windowHeight / 2, //图片上边距
					_img_left: wx.getSystemInfoSync().windowWidth / 2, //图片左边距
					watch: {
						//监听截取框宽高变化
						width(value, that) {
							if (value < that.data.min_width) {
								that.setData({
									width: that.data.min_width,
								})
							}
							that._computeCutSize()
						},
						height(value, that) {
							if (value < that.data.min_height) {
								that.setData({
									height: that.data.min_height,
								})
							}
							that._computeCutSize()
						},
						angle(value, that) {
							//停止居中裁剪框，继续修改图片位置
							that._moveStop()
							if (that.data.limit_move) {
								if (that.data.angle % 90) {
									that.setData({
										angle: Math.round(that.data.angle / 90) * 90,
									})
									return
								}
							}
						},
						_cut_animation(value, that) {
							//开启过渡300毫秒之后自动关闭
							clearTimeout(that.data._cut_animation_time)
							if (value) {
								that.data._cut_animation_time = setTimeout(() => {
									that.setData({
										_cut_animation: false,
									})
								}, 300)
							}
						},
						limit_move(value, that) {
							if (value) {
								if (that.data.angle % 90) {
									that.setData({
										angle: Math.round(that.data.angle / 90) * 90,
									})
								}
								that._imgMarginDetectionScale()
								!that.data._canvas_overflow && that._draw()
							}
						},
						canvas_top(value, that) {
							that._canvasDetectionPosition()
						},
						canvas_left(value, that) {
							that._canvasDetectionPosition()
						},
						imgSrc(value, that) {
							that.pushImg()
						},
						cut_top(value, that) {
							that._cutDetectionPosition()
							if (that.data.limit_move) {
								!that.data._canvas_overflow && that._draw()
							}
						},
						cut_left(value, that) {
							that._cutDetectionPosition()
							if (that.data.limit_move) {
								!that.data._canvas_overflow && that._draw()
							}
						}
					}
				}	
			}
		},
		methods:{
			setData(obj){
				let _this = this;
				obj.keys.map(item,function(){
					this.data[item] = obj[item]
				})
			},
			/**
			     * 上传图片
			     */
			    upload() {
			      wx.chooseImage({
			        count: 1,
			        sizeType: ['original', 'compressed'],
			        sourceType: ['album', 'camera'],
			        success: (res) => {
			          const tempFilePaths = res.tempFilePaths[0]
			          this.pushImg(tempFilePaths)
			          wx.showLoading({
			            title: '加载中...',
			          })
			        },
			      })
			    },
			    /**
			     * 返回图片信息
			     */
			    getImg(getCallback) {
			      this._draw(() => {
			        wx.canvasToTempFilePath(
			          {
			            width: this.data.width * this.data.export_scale,
			            height: Math.round(this.data.height * this.data.export_scale),
			            destWidth: this.data.width * this.data.export_scale,
			            destHeight: Math.round(this.data.height) * this.data.export_scale,
			            fileType: 'png',
			            quality: this.data.quality,
			            canvasId: this.data.el,
			            success: (res) => {
			              getCallback({
			                url: res.tempFilePath,
			                width: this.data.width * this.data.export_scale,
			                height: this.data.height * this.data.export_scale,
			              })
			            },
			          },
			          this,
			        )
			      })
			    },
			    /**
			     * 设置图片动画
			     * {
			     *    x:10,//图片在原有基础上向下移动10px
			     *    y:10,//图片在原有基础上向右移动10px
			     *    angle:10,//图片在原有基础上旋转10deg
			     *    scale:0.5,//图片在原有基础上增加0.5倍
			     * }
			     */
			    setTransform(transform) {
			      if (!transform) return
			      if (!this.data.disable_rotate) {
			        this.setData({
			          angle: transform.angle ? this.data.angle + transform.angle : this.data.angle,
			        })
			      }
			      let scale = this.data.scale
			      if (transform.scale) {
			        scale = this.data.scale + transform.scale
			        scale = scale <= this.data.min_scale ? this.data.min_scale : scale
			        scale = scale >= this.data.max_scale ? this.data.max_scale : scale
			      }
			      this.data.scale = scale
			      const cutX = this.data.cut_left
			      const cutY = this.data.cut_top
			      if (transform.cutX) {
			        this.setData({
			          cut_left: cutX + transform.cutX,
			        })
			        this.data.watch.cut_left(null, this)
			      }
			      if (transform.cutY) {
			        this.setData({
			          cut_top: cutY + transform.cutY,
			        })
			        this.data.watch.cut_top(null, this)
			      }
			      this.data._img_top = transform.y ? this.data._img_top + transform.y : this.data._img_top
			      this.data._img_left = transform.x ? this.data._img_left + transform.x : this.data._img_left
			      //图像边缘检测,防止截取到空白
			      this._imgMarginDetectionScale()
			      //停止居中裁剪框，继续修改图片位置
			      this._moveDuring()
			      this.setData({
			        scale: this.data.scale,
			        _img_top: this.data._img_top,
			        _img_left: this.data._img_left,
			      })
			      !this.data._canvas_overflow && this._draw()
			      //可以居中裁剪框了
			      this._moveStop() //结束操作
			    },
			    /**
			     * 设置剪裁框位置
			     */
			    setCutXY(x, y) {
			      this.setData({
			        cut_top: y,
			        cut_left: x,
			      })
			    },
			    /**
			     * 设置剪裁框尺寸
			     */
			    setCutSize(w, h) {
			      this.setData({
			        width: w,
			        height: h,
			      })
			      this._computeCutSize()
			    },
			    /**
			     * 设置剪裁框和图片居中
			     */
			    setCutCenter() {
			      const cut_top = (this.data.info.windowHeight - this.data.height) * 0.5
			      const cut_left = (this.data.info.windowWidth - this.data.width) * 0.5
			      //顺序不能变
			      this.setData({
			        _img_top: this.data._img_top - this.data.cut_top + cut_top,
			        cut_top: cut_top, //截取的框上边距
			        _img_left: this.data._img_left - this.data.cut_left + cut_left,
			        cut_left: cut_left, //截取的框左边距
			      })
			    },
			    _setCutCenter() {
			      const cut_top = (this.data.info.windowHeight - this.data.height) * 0.5
			      const cut_left = (this.data.info.windowWidth - this.data.width) * 0.5
			      this.setData({
			        cut_top: cut_top, //截取的框上边距
			        cut_left: cut_left, //截取的框左边距
			      })
			    },
			    /**
			     * 设置剪裁框宽度-即将废弃
			     */
			    setWidth(width) {
			      this.setData({
			        width: width,
			      })
			      this._computeCutSize()
			    },
			    /**
			     * 设置剪裁框高度-即将废弃
			     */
			    setHeight(height) {
			      this.setData({
			        height: height,
			      })
			      this._computeCutSize()
			    },
			    /**
			     * 是否锁定旋转
			     */
			    setDisableRotate(value) {
			      this.data.disable_rotate = value
			    },
			    /**
			     * 是否限制移动
			     */
			    setLimitMove(value) {
			      this.setData({
			        _cut_animation: true,
			        limit_move: !!value,
			      })
			    },
			    /**
			     * 初始化图片，包括位置、大小、旋转角度
			     */
			    imgReset() {
			      this.setData({
			        scale: 1,
			        angle: 0,
			        _img_top: wx.getSystemInfoSync().windowHeight / 2,
			        _img_left: wx.getSystemInfoSync().windowWidth / 2,
			      })
			    },
			    /**
			     * 加载（更换）图片
			     */
			    pushImg(src) {
			      if (src) {
			        this.setData({
			          imgSrc: src,
			        })
			        //发现是手动赋值直接返回，交给watch处理
			        return
			      }
			
			      // getImageInfo接口传入 src: '' 会导致内存泄漏
			
			      if (!this.data.imgSrc) return
			      wx.getImageInfo({
			        src: this.data.imgSrc,
			        success: (res) => {
			          this.data.imageObject = res
			          //图片非本地路径需要换成本地路径
			          if (this.data.imgSrc.search(/tmp/) == -1) {
			            this.setData({
			              imgSrc: res.path,
			            })
			          }
			          //计算最后图片尺寸
			          this._imgComputeSize()
			          if (this.data.limit_move) {
			            //限制移动，不留空白处理
			            this._imgMarginDetectionScale()
			          }
			          this._draw()
			        },
			        fail: () => {
			          this.setData({
			            imgSrc: '',
			          })
			        },
			      })
			    },
			    imageLoad() {
			      setTimeout(() => {
			        this.triggerEvent('imageload', this.data.imageObject)
			      }, 1000)
			    },
			    /**
			     * 设置图片放大缩小
			     */
			    setScale(scale) {
			      if (!scale) return
			      this.setData({
			        scale: scale,
			      })
			      !this.data._canvas_overflow && this._draw()
			    },
			    /**
			     * 设置图片旋转角度
			     */
			    setAngle(angle) {
			      if (!angle) return
			      this.setData({
			        _cut_animation: true,
			        angle: angle,
			      })
			      this._imgMarginDetectionScale()
			      !this.data._canvas_overflow && this._draw()
			    },
			    _initCanvas() {
			      //初始化canvas
			      if (!this.data.ctx) {
			        this.data.ctx = wx.createCanvasContext('image-cropper', this)
			      }
			    },
			    /**
			     * 根据开发者设置的图片目标尺寸计算实际尺寸
			     */
			    _initImageSize() {
			      //处理宽高特殊单位 %>px
			      if (
			        this.data.INIT_IMGWIDTH &&
			        typeof this.data.INIT_IMGWIDTH == 'string' &&
			        this.data.INIT_IMGWIDTH.indexOf('%') != -1
			      ) {
			        const width = this.data.INIT_IMGWIDTH.replace('%', '')
			        this.data.INIT_IMGWIDTH = this.data.img_width = (this.data.info.windowWidth / 100) * width
			      }
			      if (
			        this.data.INIT_IMGHEIGHT &&
			        typeof this.data.INIT_IMGHEIGHT == 'string' &&
			        this.data.INIT_IMGHEIGHT.indexOf('%') != -1
			      ) {
			        const height = this.data.img_height.replace('%', '')
			        this.data.INIT_IMGHEIGHT = this.data.img_height =
			          (this.data.info.windowHeight / 100) * height
			      }
			    },
			    /**
			     * 检测剪裁框位置是否在允许的范围内(屏幕内)
			     */
			    _cutDetectionPosition() {
			      const _cutDetectionPositionTop = () => {
			          //检测上边距是否在范围内
			          if (this.data.cut_top < 0) {
			            this.setData({
			              cut_top: 0,
			            })
			          }
			          if (this.data.cut_top > this.data.info.windowHeight - this.data.height) {
			            this.setData({
			              cut_top: this.data.info.windowHeight - this.data.height,
			            })
			          }
			        },
			        _cutDetectionPositionLeft = () => {
			          //检测左边距是否在范围内
			          if (this.data.cut_left < 0) {
			            this.setData({
			              cut_left: 0,
			            })
			          }
			          if (this.data.cut_left > this.data.info.windowWidth - this.data.width) {
			            this.setData({
			              cut_left: this.data.info.windowWidth - this.data.width,
			            })
			          }
			        }
			      //裁剪框坐标处理（如果只写一个参数则另一个默认为0，都不写默认居中）
			      if (this.data.cut_top == null && this.data.cut_left == null) {
			        this._setCutCenter()
			      } else if (this.data.cut_top != null && this.data.cut_left != null) {
			        _cutDetectionPositionTop()
			        _cutDetectionPositionLeft()
			      } else if (this.data.cut_top != null && this.data.cut_left == null) {
			        _cutDetectionPositionTop()
			        this.setData({
			          cut_left: (this.data.info.windowWidth - this.data.width) / 2,
			        })
			      } else if (this.data.cut_top == null && this.data.cut_left != null) {
			        _cutDetectionPositionLeft()
			        this.setData({
			          cut_top: (this.data.info.windowHeight - this.data.height) / 2,
			        })
			      }
			    },
			    /**
			     * 检测canvas位置是否在允许的范围内(屏幕内)如果在屏幕外则不开启实时渲染
			     * 如果只写一个参数则另一个默认为0，都不写默认超出屏幕外
			     */
			    _canvasDetectionPosition() {
			      if (this.data.canvas_top == null && this.data.canvas_left == null) {
			        this.data._canvas_overflow = false
			        this.setData({
			          canvas_top: -5000,
			          canvas_left: -5000,
			        })
			      } else if (this.data.canvas_top != null && this.data.canvas_left != null) {
			        if (
			          this.data.canvas_top < -this.data.height ||
			          this.data.canvas_top > this.data.info.windowHeight
			        ) {
			          this.data._canvas_overflow = true
			        } else {
			          this.data._canvas_overflow = false
			        }
			      } else if (this.data.canvas_top != null && this.data.canvas_left == null) {
			        this.setData({
			          canvas_left: 0,
			        })
			      } else if (this.data.canvas_top == null && this.data.canvas_left != null) {
			        this.setData({
			          canvas_top: 0,
			        })
			        if (
			          this.data.canvas_left < -this.data.width ||
			          this.data.canvas_left > this.data.info.windowWidth
			        ) {
			          this.data._canvas_overflow = true
			        } else {
			          this.data._canvas_overflow = false
			        }
			      }
			    },
			    /**
			     * 图片边缘检测-位置
			     */
			    _imgMarginDetectionPosition(scale = 1) {
			      if (!this.data.limit_move) return
			      let left = this.data._img_left
			      let top = this.data._img_top
			      scale = scale || this.data.scale
			      let img_width = this.data.img_width
			      let img_height = this.data.img_height
			      if ((this.data.angle / 90) % 2) {
			        img_width = this.data.img_height
			        img_height = this.data.img_width
			      }
			      left =
			        this.data.cut_left + (img_width * scale) / 2 >= left
			          ? left
			          : this.data.cut_left + (img_width * scale) / 2
			      left =
			        this.data.cut_left + this.data.width - (img_width * scale) / 2 <= left
			          ? left
			          : this.data.cut_left + this.data.width - (img_width * scale) / 2
			      top =
			        this.data.cut_top + (img_height * scale) / 2 >= top
			          ? top
			          : this.data.cut_top + (img_height * scale) / 2
			      top =
			        this.data.cut_top + this.data.height - (img_height * scale) / 2 <= top
			          ? top
			          : this.data.cut_top + this.data.height - (img_height * scale) / 2
			      this.setData({
			        _img_left: left,
			        _img_top: top,
			        scale: scale,
			      })
			    },
			    /**
			     * 图片边缘检测-缩放
			     */
			    _imgMarginDetectionScale() {
			      if (!this.data.limit_move) return
			      let scale = this.data.scale
			      let img_width = this.data.img_width
			      let img_height = this.data.img_height
			      if ((this.data.angle / 90) % 2) {
			        img_width = this.data.img_height
			        img_height = this.data.img_width
			      }
			      if (img_width * scale < this.data.width) {
			        scale = this.data.width / img_width
			      }
			      if (img_height * scale < this.data.height) {
			        scale = Math.max(scale, this.data.height / img_height)
			      }
			      this._imgMarginDetectionPosition(scale)
			    },
			    _setData(obj) {
			      const data = {}
			      for (const key in obj) {
			        if (this.data[key] != obj[key]) {
			          // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
			          // @ts-ignore
			          data[key] = obj[key]
			        }
			      }
			      this.setData(data)
			      return data
			    },
			    /**
			     * 计算图片尺寸
			     */
			    _imgComputeSize() {
			      let img_width = this.data.img_width,
			        img_height = this.data.img_height
			      if (!this.data.INIT_IMGHEIGHT && !this.data.INIT_IMGWIDTH) {
			        //默认按图片最小边 = 对应裁剪框尺寸
			        img_width = this.data.imageObject.width
			        img_height = this.data.imageObject.height
			        if (img_width / img_height > this.data.width / this.data.height) {
			          img_height = this.data.height
			          img_width = (this.data.imageObject.width / this.data.imageObject.height) * img_height
			        } else {
			          img_width = this.data.width
			          img_height = (this.data.imageObject.height / this.data.imageObject.width) * img_width
			        }
			      } else if (this.data.INIT_IMGHEIGHT && !this.data.INIT_IMGWIDTH) {
			        img_width =
			          (this.data.imageObject.width / this.data.imageObject.height) * this.data.INIT_IMGHEIGHT
			      } else if (!this.data.INIT_IMGHEIGHT && this.data.INIT_IMGWIDTH) {
			        img_height =
			          (this.data.imageObject.height / this.data.imageObject.width) * this.data.INIT_IMGWIDTH
			      }
			      this.setData({
			        img_width: img_width,
			        img_height: img_height,
			      })
			    },
			    //改变截取框大小
			    _computeCutSize() {
			      if (this.data.width > this.data.info.windowWidth) {
			        this.setData({
			          width: this.data.info.windowWidth,
			        })
			      } else if (this.data.width + this.data.cut_left > this.data.info.windowWidth) {
			        this.setData({
			          cut_left: this.data.info.windowWidth - this.data.cut_left,
			        })
			      }
			      if (this.data.height > this.data.info.windowHeight) {
			        this.setData({
			          height: this.data.info.windowHeight,
			        })
			      } else if (this.data.height + this.data.cut_top > this.data.info.windowHeight) {
			        this.setData({
			          cut_top: this.data.info.windowHeight - this.data.cut_top,
			        })
			      }
			      !this.data._canvas_overflow && this._draw()
			    },
			    //开始触摸
			    _start(event) {
			      this.data._flag_img_endtouch = false
			      if (event.touches.length == 1) {
			        //单指拖动
			        this.data._touch_img_relative[0] = {
			          x: event.touches[0].clientX - this.data._img_left,
			          y: event.touches[0].clientY - this.data._img_top,
			        }
			      } else {
			        //双指放大
			        const width = Math.abs(event.touches[0].clientX - event.touches[1].clientX)
			        const height = Math.abs(event.touches[0].clientY - event.touches[1].clientY)
			        this.data._touch_img_relative = [
			          {
			            x: event.touches[0].clientX - this.data._img_left,
			            y: event.touches[0].clientY - this.data._img_top,
			          },
			          {
			            x: event.touches[1].clientX - this.data._img_left,
			            y: event.touches[1].clientY - this.data._img_top,
			          },
			        ]
			        this.data._hypotenuse_length = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2))
			      }
			      !this.data._canvas_overflow && this._draw()
			    },
			    _move_throttle() {
			      //安卓需要节流
			      if (this.data.info.platform == 'android') {
			        clearTimeout(this.data.MOVE_THROTTLE)
			        this.data.MOVE_THROTTLE = setTimeout(() => {
			          this.data.MOVE_THROTTLE_FLAG = true
			        }, 1000 / 40)
			        return this.data.MOVE_THROTTLE_FLAG
			      } else {
			        this.data.MOVE_THROTTLE_FLAG = true
			      }
			    },
			    _move(event) {
			      if (this.data._flag_img_endtouch || !this.data.MOVE_THROTTLE_FLAG) return
			      this.data.MOVE_THROTTLE_FLAG = false
			      this._move_throttle()
			      this._moveDuring()
			      if (event.touches.length == 1) {
			        //单指拖动
			        const left = event.touches[0].clientX - this.data._touch_img_relative[0].x,
			          top = event.touches[0].clientY - this.data._touch_img_relative[0].y
			        //图像边缘检测,防止截取到空白
			        this.data._img_left = left
			        this.data._img_top = top
			        this._imgMarginDetectionPosition()
			        this.setData({
			          _img_left: this.data._img_left,
			          _img_top: this.data._img_top,
			        })
			      } else {
			        //双指放大
			        const width = Math.abs(event.touches[0].clientX - event.touches[1].clientX),
			          height = Math.abs(event.touches[0].clientY - event.touches[1].clientY),
			          hypotenuse = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2))
			
			        let scale = this.data.scale * (hypotenuse / this.data._hypotenuse_length),
			          current_deg = 0
			        scale = scale <= this.data.min_scale ? this.data.min_scale : scale
			        scale = scale >= this.data.max_scale ? this.data.max_scale : scale
			        //图像边缘检测,防止截取到空白
			        this.data.scale = scale
			        this._imgMarginDetectionScale()
			        //双指旋转(如果没禁用旋转)
			        const _touch_img_relative = [
			          {
			            x: event.touches[0].clientX - this.data._img_left,
			            y: event.touches[0].clientY - this.data._img_top,
			          },
			          {
			            x: event.touches[1].clientX - this.data._img_left,
			            y: event.touches[1].clientY - this.data._img_top,
			          },
			        ]
			        if (!this.data.disable_rotate) {
			          const first_atan =
			            (180 / Math.PI) * Math.atan2(_touch_img_relative[0].y, _touch_img_relative[0].x)
			          const first_atan_old =
			            (180 / Math.PI) *
			            Math.atan2(this.data._touch_img_relative[0].y, this.data._touch_img_relative[0].x)
			          const second_atan =
			            (180 / Math.PI) * Math.atan2(_touch_img_relative[1].y, _touch_img_relative[1].x)
			          const second_atan_old =
			            (180 / Math.PI) *
			            Math.atan2(this.data._touch_img_relative[1].y, this.data._touch_img_relative[1].x)
			          //当前旋转的角度
			          const first_deg = first_atan - first_atan_old,
			            second_deg = second_atan - second_atan_old
			          if (first_deg != 0) {
			            current_deg = first_deg
			          } else if (second_deg != 0) {
			            current_deg = second_deg
			          }
			        }
			        this.data._touch_img_relative = _touch_img_relative
			        this.data._hypotenuse_length = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2))
			        //更新视图
			        this.setData({
			          angle: this.data.angle + current_deg,
			          scale: this.data.scale,
			        })
			      }
			      !this.data._canvas_overflow && this._draw()
			    },
			    //结束操作
			    _end() {
			      this.data._flag_img_endtouch = true
			      this._moveStop()
			    },
			    //点击中间剪裁框处理
			    _click(event) {
			      if (!this.data.imgSrc) {
			        //调起上传
			        this.upload()
			        return
			      }
			      this._draw(() => {
			        const x = event.detail ? event.detail.x : event.touches[0].clientX
			        const y = event.detail ? event.detail.y : event.touches[0].clientY
			        if (
			          x >= this.data.cut_left &&
			          x <= this.data.cut_left + this.data.width &&
			          y >= this.data.cut_top &&
			          y <= this.data.cut_top + this.data.height
			        ) {
			          //生成图片并回调
			          wx.canvasToTempFilePath(
			            {
			              width: this.data.width * this.data.export_scale,
			              height: Math.round(this.data.height * this.data.export_scale),
			              destWidth: this.data.width * this.data.export_scale,
			              destHeight: Math.round(this.data.height) * this.data.export_scale,
			              fileType: 'png',
			              quality: this.data.quality,
			              canvasId: this.data.el,
			              success: (res) => {
			                this.triggerEvent('tapcut', {
			                  url: res.tempFilePath,
			                  width: this.data.width * this.data.export_scale,
			                  height: this.data.height * this.data.export_scale,
			                })
			              },
			            },
			            this,
			          )
			        }
			      })
			    },
			    //渲染
			    _draw(callback) {
			      if (!this.data.imgSrc) return
			      const draw = () => {
			        //图片实际大小
			        const img_width = this.data.img_width * this.data.scale * this.data.export_scale
			        const img_height = this.data.img_height * this.data.scale * this.data.export_scale
			        //canvas和图片的相对距离
			        const xpos = this.data._img_left - this.data.cut_left
			        const ypos = this.data._img_top - this.data.cut_top
			        //旋转画布
			        this.data.ctx.translate(xpos * this.data.export_scale, ypos * this.data.export_scale)
			        this.data.ctx.rotate((this.data.angle * Math.PI) / 180)
			        this.data.ctx.drawImage(
			          this.data.imgSrc,
			          -img_width / 2,
			          -img_height / 2,
			          img_width,
			          img_height,
			        )
			        this.data.ctx.draw(false, () => {
			          callback && callback()
			        })
			      }
			      if (this.data.ctx.width != this.data.width || this.data.ctx.height != this.data.height) {
			        //优化拖动裁剪框，所以必须把宽高设置放在离用户触发渲染最近的地方
			        this.setData(
			          {
			            _canvas_height: this.data.height,
			            _canvas_width: this.data.width,
			          },
			          () => {
			            //延迟40毫秒防止点击过快出现拉伸或裁剪过多
			            setTimeout(() => {
			              draw()
			            }, 40)
			          },
			        )
			      } else {
			        draw()
			      }
			    },
			    //裁剪框处理
			    _cutTouchMove(e) {
			      if (this.data._flag_cut_touch && this.data.MOVE_THROTTLE_FLAG) {
			        if (this.data.disable_ratio && (this.data.disable_width || this.data.disable_height)) return
			        //节流
			        this.data.MOVE_THROTTLE_FLAG = false
			        this._move_throttle()
			        let width = this.data.width,
			          height = this.data.height,
			          cut_top = this.data.cut_top,
			          cut_left = this.data.cut_left
			        const size_correct = () => {
			            width =
			              width <= this.data.max_width
			                ? width >= this.data.min_width
			                  ? width
			                  : this.data.min_width
			                : this.data.max_width
			            height =
			              height <= this.data.max_height
			                ? height >= this.data.min_height
			                  ? height
			                  : this.data.min_height
			                : this.data.max_height
			          },
			          size_inspect = () => {
			            if (
			              (width > this.data.max_width ||
			                width < this.data.min_width ||
			                height > this.data.max_height ||
			                height < this.data.min_height) &&
			              this.data.disable_ratio
			            ) {
			              size_correct()
			              return false
			            } else {
			              size_correct()
			              return true
			            }
			          }
			        height =
			          this.data.CUT_START.height +
			          (this.data.CUT_START.corner > 1 && this.data.CUT_START.corner < 4 ? 1 : -1) *
			            (this.data.CUT_START.y - e.touches[0].clientY)
			        switch (this.data.CUT_START.corner) {
			          case 1:
			            width = this.data.CUT_START.width + this.data.CUT_START.x - e.touches[0].clientX
			            if (this.data.disable_ratio) {
			              height = width / (this.data.width / this.data.height)
			            }
			            if (!size_inspect()) return
			            cut_left = this.data.CUT_START.cut_left - (width - this.data.CUT_START.width)
			            break
			          case 2:
			            width = this.data.CUT_START.width + this.data.CUT_START.x - e.touches[0].clientX
			            if (this.data.disable_ratio) {
			              height = width / (this.data.width / this.data.height)
			            }
			            if (!size_inspect()) return
			            cut_top = this.data.CUT_START.cut_top - (height - this.data.CUT_START.height)
			            cut_left = this.data.CUT_START.cut_left - (width - this.data.CUT_START.width)
			            break
			          case 3:
			            width = this.data.CUT_START.width - this.data.CUT_START.x + e.touches[0].clientX
			            if (this.data.disable_ratio) {
			              height = width / (this.data.width / this.data.height)
			            }
			            if (!size_inspect()) return
			            cut_top = this.data.CUT_START.cut_top - (height - this.data.CUT_START.height)
			            break
			          case 4:
			            width = this.data.CUT_START.width - this.data.CUT_START.x + e.touches[0].clientX
			            if (this.data.disable_ratio) {
			              height = width / (this.data.width / this.data.height)
			            }
			            if (!size_inspect()) return
			            break
			        }
			        if (!this.data.disable_width && !this.data.disable_height) {
			          this.setData({
			            width: width,
			            cut_left: cut_left,
			            height: height,
			            cut_top: cut_top,
			          })
			        } else if (!this.data.disable_width) {
			          this.setData({
			            width: width,
			            cut_left: cut_left,
			          })
			        } else if (!this.data.disable_height) {
			          this.setData({
			            height: height,
			            cut_top: cut_top,
			          })
			        }
			        this._imgMarginDetectionScale()
			      }
			    },
			    _cutTouchStart(e) {
			      const currentX = e.touches[0].clientX
			      const currentY = e.touches[0].clientY
			      const cutbox_top4 = this.data.cut_top + this.data.height - 30
			      const cutbox_bottom4 = this.data.cut_top + this.data.height + 20
			      const cutbox_left4 = this.data.cut_left + this.data.width - 30
			      const cutbox_right4 = this.data.cut_left + this.data.width + 30
			
			      const cutbox_top3 = this.data.cut_top - 30
			      const cutbox_bottom3 = this.data.cut_top + 30
			      const cutbox_left3 = this.data.cut_left + this.data.width - 30
			      const cutbox_right3 = this.data.cut_left + this.data.width + 30
			
			      const cutbox_top2 = this.data.cut_top - 30
			      const cutbox_bottom2 = this.data.cut_top + 30
			      const cutbox_left2 = this.data.cut_left - 30
			      const cutbox_right2 = this.data.cut_left + 30
			
			      const cutbox_top1 = this.data.cut_top + this.data.height - 30
			      const cutbox_bottom1 = this.data.cut_top + this.data.height + 30
			      const cutbox_left1 = this.data.cut_left - 30
			      const cutbox_right1 = this.data.cut_left + 30
			      if (
			        currentX > cutbox_left4 &&
			        currentX < cutbox_right4 &&
			        currentY > cutbox_top4 &&
			        currentY < cutbox_bottom4
			      ) {
			        this._moveDuring()
			        this.data._flag_cut_touch = true
			        this.data._flag_img_endtouch = true
			        this.data.CUT_START = {
			          width: this.data.width,
			          height: this.data.height,
			          x: currentX,
			          y: currentY,
			          corner: 4,
			        }
			      } else if (
			        currentX > cutbox_left3 &&
			        currentX < cutbox_right3 &&
			        currentY > cutbox_top3 &&
			        currentY < cutbox_bottom3
			      ) {
			        this._moveDuring()
			        this.data._flag_cut_touch = true
			        this.data._flag_img_endtouch = true
			        this.data.CUT_START = {
			          width: this.data.width,
			          height: this.data.height,
			          x: currentX,
			          y: currentY,
			          cut_top: this.data.cut_top,
			          cut_left: this.data.cut_left,
			          corner: 3,
			        }
			      } else if (
			        currentX > cutbox_left2 &&
			        currentX < cutbox_right2 &&
			        currentY > cutbox_top2 &&
			        currentY < cutbox_bottom2
			      ) {
			        this._moveDuring()
			        this.data._flag_cut_touch = true
			        this.data._flag_img_endtouch = true
			        this.data.CUT_START = {
			          width: this.data.width,
			          height: this.data.height,
			          cut_top: this.data.cut_top,
			          cut_left: this.data.cut_left,
			          x: currentX,
			          y: currentY,
			          corner: 2,
			        }
			      } else if (
			        currentX > cutbox_left1 &&
			        currentX < cutbox_right1 &&
			        currentY > cutbox_top1 &&
			        currentY < cutbox_bottom1
			      ) {
			        this._moveDuring()
			        this.data._flag_cut_touch = true
			        this.data._flag_img_endtouch = true
			        this.data.CUT_START = {
			          width: this.data.width,
			          height: this.data.height,
			          cut_top: this.data.cut_top,
			          cut_left: this.data.cut_left,
			          x: currentX,
			          y: currentY,
			          corner: 1,
			        }
			      }
			    },
			    _cutTouchEnd() {
			      this._moveStop()
			      this.data._flag_cut_touch = false
			    },
			    //停止移动时需要做的操作
			    _moveStop() {
			      //清空之前的自动居中延迟函数并添加最新的
			      clearTimeout(this.data.TIME_CUT_CENTER)
			      this.data.TIME_CUT_CENTER = setTimeout(() => {
			        //动画启动
			        if (!this.data._cut_animation) {
			          this.setData({
			            _cut_animation: true,
			          })
			        }
			        this.setCutCenter()
			      }, 1000)
			      //清空之前的背景变化延迟函数并添加最新的
			      clearTimeout(this.data.TIME_BG)
			      this.data.TIME_BG = setTimeout(() => {
			        if (this.data._flag_bright) {
			          this.setData({
			            _flag_bright: false,
			          })
			        }
			      }, 2000)
			    },
			    //移动中
			    _moveDuring() {
			      //清空之前的自动居中延迟函数
			      clearTimeout(this.data.TIME_CUT_CENTER)
			      //清空之前的背景变化延迟函数
			      clearTimeout(this.data.TIME_BG)
			      //高亮背景
			      if (!this.data._flag_bright) {
			        this.setData({
			          _flag_bright: true,
			        })
			      }
			    },
			    //监听器
			    _$watcher() {
			      Object.keys(this.data).forEach((v) => {
			        this._observe(this.data, v, this.data.watch[v])
			      })
			    },
			    _observe(obj, key, watchFun) {
			      let val = obj[key]
			      Object.defineProperty(obj, key, {
			        configurable: true,
			        enumerable: true,
			        set: (value) => {
			          val = value
			          watchFun && watchFun(val, this)
			        },
			        get() {
			          if (
			            val &&
			            '_img_top|img_left||width|height|min_width|max_width|min_height|max_height|export_scale|cut_top|cut_left|canvas_top|canvas_left|img_width|img_height|scale|angle|min_scale|max_scale'.indexOf(
			              key,
			            ) != -1
			          ) {
			            let ret = parseFloat(parseFloat(val).toFixed(3))
			            if (typeof val == 'string' && val.indexOf('%') != -1) {
			              ret += '%'
			            }
			            return ret
			          }
			          return val
			        },
			      })
			    },
			    // eslint-disable-next-line @typescript-eslint/no-empty-function
			    _preventTouchMove() {},
			  
		}
	}
	
</script>

<style lang="scss">
	.image-cropper {
		position: fixed;
		top: 0;
		left: 0;
		z-index: 100;
		width: 100vw;
		height: 100vh;
		background: rgba(14, 13, 13, 0.8);
	}

	.main {
		position: relative;
		width: 100vw;
		height: 100vh;
		overflow: hidden;
	}

	.content {
		z-index: 109;
		position: absolute;
		width: 100vw;
		height: 100vh;
		display: flex;
		flex-direction: column;
		pointer-events: none;
	}

	.bg_black {
		background: rgba(0, 0, 0, 0.8) !important;
	}

	.bg_gray {
		background: rgba(0, 0, 0, 0.45);
		transition-duration: 0.35s;
	}

	.content>.content_top {
		pointer-events: none;
	}

	.content>.content_middle {
		display: flex;
		height: 200px;
		width: 100%;
	}

	.content_middle_middle {
		width: 200px;
		box-sizing: border-box;
		position: relative;
		transition-duration: 0.3s;
	}

	.content_middle_right {
		flex: auto;
	}

	.content>.content_bottom {
		flex: auto;
	}

	.image-cropper .img {
		z-index: 102;
		top: 0;
		left: 0;
		position: absolute;
		border: none;
		width: 100%;
		backface-visibility: hidden;
		transform-origin: center;
	}

	.image-cropper-canvas {
		position: fixed;
		background: white;
		width: 150px;
		height: 150px;
		z-index: 110;
		top: -200%;
		pointer-events: none;
	}

	.border {
		background: white;
		pointer-events: auto;
		position: absolute;
	}

	.border-top-left {
		left: -2.5px;
		top: -2.5px;
		height: 2.5px;
		width: 33rpx;
	}

	.border-top-right {
		right: -2.5px;
		top: -2.5px;
		height: 2.5px;
		width: 33rpx;
	}

	.border-right-top {
		top: -1px;
		width: 2.5px;
		height: 30rpx;
		right: -2.5px;
	}

	.border-right-bottom {
		width: 2.5px;
		height: 30rpx;
		right: -2.5px;
		bottom: -1px;
	}

	.border-bottom-left {
		height: 2.5px;
		width: 33rpx;
		bottom: -2.5px;
		left: -2.5px;
	}

	.border-bottom-right {
		height: 2.5px;
		width: 33rpx;
		bottom: -2.5px;
		right: -2.5px;
	}

	.border-left-top {
		top: -1px;
		width: 2.5px;
		height: 30rpx;
		left: -2.5px;
	}

	.border-left-bottom {
		width: 2.5px;
		height: 30rpx;
		left: -2.5px;
		bottom: -1px;
	}
</style>
